using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Security.Cryptography;
using System.Net;
using System.Collections.Specialized;
using System.Threading;
using System.Diagnostics;
using System.Drawing;

//using PocketLibrary.Image.;

namespace PocketLibrary.Image
{
    public class flickr
    {
        public event flickrResponseHandler OnResponse;
        public event flickrResponseHandler OnContactListResponse;
        public event flickrResponseHandler OnPhotoSetListResponse;
        public event flickrResponseHandler OnPhotoListResponse;
        public event flickrErrorHandler OnErrorResponse;

        private string restUrl = "http://api.flickr.com/services/rest/";
        private string apiKey = "068a1628b7b5ac8522e070f7d7dd3039";
        private string sharedSecret = "1c728e2bc026fb42";
        private string authToken = "fail";
        List<flickrPhotoset> _setlist = new List<flickrPhotoset>();
        List<flickrPhoto> _photolist = new List<flickrPhoto>();
        List<flickrContact> _contactlist = new List<flickrContact>();

        public List<flickrPhotoset> PhotoSets
        {
            get
            {
                return _setlist;
            }
        }

        public List<flickrPhoto> CurrentPhotos
        {
            get
            {
                return _photolist;
            }
        }

        public List<flickrContact> CurrentContacts
        {
            get { return _contactlist; }
        }

        public string MiniToken = "";

        private string _nsid;
        public string NSID
        {
            get { return _nsid; }
        }


        long photosize = 0;

        public long PhotoFilesize
        {
            get
            {
                return photosize;
            }
        }

        public flickr(string token)
        {
            authToken = token;
            if (authToken != null && authToken != string.Empty)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(flickrCheckToken));
            }
        }

        public flickr()
        {
            if (authToken != null && authToken != string.Empty)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(flickrCheckToken));
            }
        }

        #region helper methods

        private string ByteArrayToHexString(byte[] data)
        {
            StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data)
                sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
            return sb.ToString().ToLower();
        }

        private string SignRequest(string request)
        {
            //MD5 mymd5 = MD5.Create();
            //byte[] hash = mymd5.ComputeHash(Encoding.ASCII.GetBytes(request));
            //return ByteArrayToHexString(hash);
            System.Security.Cryptography.MD5CryptoServiceProvider csp = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(request);
            byte[] hashedBytes = csp.ComputeHash(bytes, 0, bytes.Length);
            return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
        }

        private HttpWebRequest CreateRequest(string uri)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            //request.PreAuthenticate = true;
            request.ProtocolVersion = new Version("1.1");
            //request.SendChunked = false;
            request.UserAgent = "TaskuBlogi 1.0";
            //request.Credentials = new NetworkCredential(UserName, PassWord);

            //byte[] _basic = Encoding.ASCII.GetBytes(UserName + ":" + PassWord);
            //string _strbasic = Convert.ToBase64String(_basic);
            //request.Headers.Add("Authorization: Basic " + _strbasic);

            return request;
        }

        private string ReadResponseBody(HttpWebResponse response)
        {
            string body;

            Stream stream = response.GetResponseStream();

            StreamReader reader = new StreamReader(stream, Encoding.UTF8);

            body = reader.ReadToEnd();

            stream.Close();

            return body;
        }

        private void WriteRequestBody(HttpWebRequest request, string body)
        {
            byte[] bytes;

            bytes = Encoding.UTF8.GetBytes(body);

            request.ContentLength = bytes.Length;

            Stream stream = request.GetRequestStream();

            stream.Write(bytes, 0, bytes.Length);

            stream.Close();
        }
        #endregion

        #region private methods

        private void flickrCheckToken(object stateInfo)
        {
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.auth.checkToken");
            hashString.Append("methodflickr.auth.checkToken");

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);

            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {

                XmlNode _nsidNode = received.SelectSingleNode("descendant::user");
                _nsid = _nsidNode.Attributes["nsid"].Value;

                if(OnResponse!=null)
                    OnResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        private void flickrPhotosetsGetList(object stateInfo)
        {
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.photosets.getList");
            hashString.Append("methodflickr.photosets.getList");

            if (stateInfo != null && (string)stateInfo != "")
            {
                string _userid = (string)stateInfo;
                requestString.Append("&user_id=" + _userid);
                hashString.Append("user_id" + _userid);
            }

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);

            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNodeList sets = received.SelectNodes("descendant::photosets/photoset");
                _setlist.Clear();
                foreach (XmlNode set in sets)
                {
                    flickrPhotoset photoset = new flickrPhotoset(
                        set.Attributes["id"].Value,
                        set.ChildNodes[0].InnerText,
                        set.ChildNodes[1].InnerText,
                        set.Attributes["photos"].Value,
                        set.Attributes["primary"].Value);
                    _setlist.Add(photoset);
                }

                if (OnPhotoSetListResponse != null)
                    OnPhotoSetListResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        private void flickrPhotosetsGetPhotos(object stateInfo)
        {
            string photosetid = (string)stateInfo;

            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.photosets.getPhotos");
            hashString.Append("methodflickr.photosets.getPhotos");

            requestString.Append("&photoset_id=" + photosetid);
            hashString.Append("photoset_id" + photosetid);

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
            <photoset id="4" primary="2483" page="1" per_page="500" pages="1" total="2">
                <photo id="2484" secret="123456" server="1" title="my photo" isprimary="0" /> 
                <photo id="2483" secret="123456" server="1" title="flickr rocks" isprimary="1" /> 
            </photoset>
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNodeList photos = received.SelectNodes("descendant::photoset/photo");
                _photolist.Clear();
                foreach (XmlNode photo in photos)
                {
                    flickrPhoto fphoto = new flickrPhoto(
                        photo.Attributes["id"].Value,
                        photo.Attributes["secret"].Value,
                        photo.Attributes["server"].Value,
                        photo.Attributes["farm"].Value,
                        photo.Attributes["title"].Value);
                    _photolist.Add(fphoto);

                }
                if (OnPhotoListResponse != null)
                    OnPhotoListResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }

        }

        private flickrPhoto[] flickPhotosGetContext(string photoid)
        {
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.photos.getContext");
            hashString.Append("methodflickr.photos.getContext");

            requestString.Append("&photo_id=" + photoid);
            hashString.Append("photo_id" + photoid);

            //requestString.Append("&photoset_id=" + photosetid);
            //hashString.Append("photoset_id" + photosetid);

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
                <count>2</count>
                <prevphoto id="0" />
                <nextphoto id="216691600" secret="f0858b189c" server="87" farm="1" title="hprx370004" url="/photos/choomba/216691600/in/set-72157594356963699/" thumb="http://farm1.static.flickr.com/87/216691600_f0858b189c_s.jpg" />
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            flickrPhoto[] retset = new flickrPhoto[2];
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNode xmlPrevPhoto = received.SelectSingleNode("descendant::prevphoto");
                XmlNode xmlNextPhoto = received.SelectSingleNode("descendant::nextphoto");

                if (xmlPrevPhoto.Attributes["id"].Value != "0")
                {
                    retset[0] = new flickrPhoto(
                        xmlPrevPhoto.Attributes["id"].Value,
                        xmlPrevPhoto.Attributes["secret"].Value,
                        xmlPrevPhoto.Attributes["server"].Value,
                        xmlPrevPhoto.Attributes["farm"].Value,
                        xmlPrevPhoto.Attributes["title"].Value);
                }
                else
                    retset[0] = new flickrPhoto();

                if (xmlNextPhoto.Attributes["id"].Value != "0")
                {
                    retset[1] = new flickrPhoto(
                            xmlNextPhoto.Attributes["id"].Value,
                            xmlNextPhoto.Attributes["secret"].Value,
                            xmlNextPhoto.Attributes["server"].Value,
                            xmlNextPhoto.Attributes["farm"].Value,
                            xmlNextPhoto.Attributes["title"].Value);
                }
                else
                    retset[1] = new flickrPhoto();

                return retset;
            }
            else
            {
                retset[0] = new flickrPhoto();
                retset[1] = new flickrPhoto();
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));

                return retset;
            }
        }

        private flickrPhoto[] flickPhotosetsGetContext(string photosetid, string photoid)
        {

            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.photosets.getContext");
            hashString.Append("methodflickr.photosets.getContext");

            requestString.Append("&photo_id=" + photoid);
            hashString.Append("photo_id" + photoid);

            requestString.Append("&photoset_id=" + photosetid);
            hashString.Append("photoset_id" + photosetid);

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
                <count>2</count>
                <prevphoto id="0" />
                <nextphoto id="216691600" secret="f0858b189c" server="87" farm="1" title="hprx370004" url="/photos/choomba/216691600/in/set-72157594356963699/" thumb="http://farm1.static.flickr.com/87/216691600_f0858b189c_s.jpg" />
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            flickrPhoto[] retset = new flickrPhoto[2];
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNode xmlPrevPhoto = received.SelectSingleNode("descendant::prevphoto");
                XmlNode xmlNextPhoto = received.SelectSingleNode("descendant::nextphoto");

                if (xmlPrevPhoto.Attributes["id"].Value != "0")
                {
                    retset[0] = new flickrPhoto(
                        xmlPrevPhoto.Attributes["id"].Value,
                        xmlPrevPhoto.Attributes["secret"].Value,
                        xmlPrevPhoto.Attributes["server"].Value,
                        xmlPrevPhoto.Attributes["farm"].Value,
                        xmlPrevPhoto.Attributes["title"].Value);
                }
                else
                    retset[0] = new flickrPhoto();

                if (xmlNextPhoto.Attributes["id"].Value != "0")
                {
                    retset[1] = new flickrPhoto(
                            xmlNextPhoto.Attributes["id"].Value,
                            xmlNextPhoto.Attributes["secret"].Value,
                            xmlNextPhoto.Attributes["server"].Value,
                            xmlNextPhoto.Attributes["farm"].Value,
                            xmlNextPhoto.Attributes["title"].Value);
                }
                else
                    retset[1] = new flickrPhoto();

                return retset;
            }
            else
            {
                retset[0] = new flickrPhoto();
                retset[1] = new flickrPhoto();

                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));

                return retset;
            }
        }

        private void flickrPhotosGetNotInSet(object stateInfo)
        {
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.photos.getNotInSet");
            hashString.Append("methodflickr.photos.getNotInSet");

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
                <photos page="1" pages="1" perpage="100" total="1">
             * <photo id="507550967" owner="69337239@N00" secret="e621ca1ce1" server="219" farm="1" title="IMAGE_194" ispublic="1" isfriend="0" isfamily="0" />
             * </photos>
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNodeList photos = received.SelectNodes("descendant::photos/photo");
                _photolist.Clear();
                foreach (XmlNode photo in photos)
                {
                    flickrPhoto fphoto = new flickrPhoto(
                        photo.Attributes["id"].Value,
                        photo.Attributes["secret"].Value,
                        photo.Attributes["server"].Value,
                        photo.Attributes["farm"].Value,
                        photo.Attributes["title"].Value);
                    _photolist.Add(fphoto);

                }
                if (OnPhotoListResponse != null)
                    OnPhotoListResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        private void flickrPhotoSetsCreate(object stateInfo)
        {
            flickrPhotoset set = (flickrPhotoset)stateInfo;
            /*
            api_key (Required)
                Your API application key. See here for more details.
            title (Required)
                A title for the photoset.
            description (Optional)
                A description of the photoset. May contain limited html.
            primary_photo_id (Required)
                The id of the photo to represent this set. The photo must belong to the calling user. 
             */
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);


            requestString.Append("?api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            if (set.Description != "")
            {
                requestString.Append("&description=" + set.Description);
                hashString.Append("description" + set.Description);
            }  
            
            requestString.Append("&method=flickr.photosets.create");
            hashString.Append("methodflickr.photosets.create");

            requestString.Append("&primary_photo_id=" + set.PrimaryPhotoId);
            hashString.Append("primary_photo_id" + set.PrimaryPhotoId);

            requestString.Append("&title=" + set.Title);
            hashString.Append("title=" + set.Title);

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));
            
            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "POST";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
            <photoset id="72157600290333232" url="http://www.flickr.com/photos/choomba/sets/72157600290333232/"/>
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNode photoset = received.SelectSingleNode("descendant::photoset");

                flickrPhotoset fphoto = new flickrPhotoset(
                    photoset.Attributes["id"].Value,
                    photoset.ChildNodes[0].InnerText,
                    photoset.ChildNodes[1].InnerText,
                    photoset.Attributes["photos"].Value,
                    photoset.Attributes["primary"].Value);
                _setlist.Insert(0, fphoto);

                if (OnPhotoSetListResponse != null)
                    OnPhotoSetListResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        private void flickrPhotoSetsDelete(object stateInfo)
        {
            string set = (string)stateInfo;
            /*
            api_key (Required)
                Your API application key. See here for more details.
            title (Required)
                A title for the photoset.
            description (Optional)
                A description of the photoset. May contain limited html.
            primary_photo_id (Required)
                The id of the photo to represent this set. The photo must belong to the calling user. 
             */
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);


            requestString.Append("?method=flickr.photosets.delete");
            hashString.Append("methodflickr.photosets.delete");

            requestString.Append("&api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append(string.Format("&photoset_id={0}",set));
            hashString.Append("photoset_id" + set);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "POST";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            Debug.WriteLine(received.InnerXml);
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                if (OnResponse != null)
                    OnResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        private void flickrContactsGetList(object stateInfo)
        {
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);

            requestString.Append("?");

            requestString.Append("api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            requestString.Append("&auth_token=" + authToken);
            hashString.Append("auth_token" + authToken);

            requestString.Append("&method=flickr.contacts.getList");
            hashString.Append("methodflickr.contacts.getList");

            requestString.Append("&api_sig=");
            requestString.Append(SignRequest(hashString.ToString()));

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
            <contacts page="1" pages="1" perpage="1000" total="3">
	            <contact nsid="12037949629@N01" username="Eric" iconserver="1"
		            realname="Eric Costello"
		            friend="1" family="0" ignored="1" /> 
	            <contact nsid="12037949631@N01" username="neb" iconserver="1"
		            realname="Ben Cerveny"
		            friend="0" family="0" ignored="0" /> 
	            <contact nsid="41578656547@N01" username="cal_abc" iconserver="1"
		            realname="Cal Henderson"
		            friend="1" family="1" ignored="0" />
            </contacts>
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNodeList contacts = received.SelectNodes("descendant::contacts/contact");
                _contactlist.Clear();
                foreach (XmlNode contact in contacts)
                {
                    flickrContact fcontact = new flickrContact(
                        contact.Attributes["nsid"].Value,
                        contact.Attributes["username"].Value,
                        contact.Attributes["realname"].Value,
                        contact.Attributes["friend"].Value,
                        contact.Attributes["family"].Value,
                        contact.Attributes["ignored"].Value);

                    fcontact.IconFarm = contact.Attributes["iconfarm"].Value;
                    fcontact.IconServer = contact.Attributes["iconserver"].Value;

                    _contactlist.Add(fcontact);

                }
                if (OnContactListResponse != null)
                    OnContactListResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        // HACK
        private void flickrPeopleGetPublicPhotos(object stateInfo)
        {
            StringBuilder requestString = new StringBuilder(restUrl);
            StringBuilder hashString = new StringBuilder(sharedSecret);
            // ?method=flickr.people.getPublicPhotos
            requestString.Append("?method=flickr.people.getPublicPhotos");
            hashString.Append("methodflickr.people.getPublicPhotos");

            // &api_key=5efc99e593e6950970d6ab7d290fafd5
            requestString.Append("&api_key=" + apiKey);
            hashString.Append("api_key" + apiKey);

            // &user_id=7755284%40N02
            string nsid = (string)stateInfo;
            nsid = nsid.Replace("@", "%40");
            requestString.Append("&user_id=" + nsid);
            hashString.Append("user_id" + nsid);

            // &auth_token=72157600264034292-738e5d267750753e
            //requestString.Append("&auth_token=" + authToken);
            //hashString.Append("auth_token" + authToken);

            // &api_sig=9d7285ac75c50173dfb23713020f4af6
            //requestString.Append("&api_sig=");
            //requestString.Append(SignRequest(hashString.ToString()));


            //?method=flickr.people.getPublicPhotos
            //&api_key=1b90397c597cfe9b9b13ac99242f8056
            //&user_id=7755284%40N02
            //&auth_token=72157600227840318-468ec13144cbc18f
            //&api_sig=83bdc3c8e0824f39ab55e16718d00aa4

            //requestString.Append("&per_page=500");
            //hashString.Append("perpage500");


            Debug.WriteLine(nsid, "user id");
            Debug.WriteLine(requestString.ToString());

            HttpWebRequest request = CreateRequest(requestString.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            /**
             <?xml version="1.0" encoding="utf-8"?>
            <rsp stat="ok">
                <photos page="1" pages="1" perpage="100" total="1">
             * <photo id="507550967" owner="69337239@N00" secret="e621ca1ce1" server="219" farm="1" title="IMAGE_194" ispublic="1" isfriend="0" isfamily="0" />
             * </photos>
            </rsp>
            /**/
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNodeList photos = received.SelectNodes("descendant::photos/photo");
                _photolist.Clear();
                foreach (XmlNode photo in photos)
                {
                    flickrPhoto fphoto = new flickrPhoto(
                        photo.Attributes["id"].Value,
                        photo.Attributes["secret"].Value,
                        photo.Attributes["server"].Value,
                        photo.Attributes["farm"].Value,
                        photo.Attributes["title"].Value);
                    _photolist.Add(fphoto);

                }
                if (OnPhotoListResponse != null)
                    OnPhotoListResponse(this, new EventArgs());
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
            }
        }

        #endregion

        #region Public Methods

        public string authgetFullToken()
        {
            StringBuilder sig = new StringBuilder();
            sig.Append(sharedSecret);
            sig.Append("api_key" + apiKey);
            sig.Append("methodflickr.auth.getFullToken");
            sig.Append("mini_token" + MiniToken);

            string signature = SignRequest(sig.ToString());

            StringBuilder req = new StringBuilder();
            req.Append("?method=flickr.auth.getFullToken");
            req.Append("&api_key=" + apiKey);
            req.Append("&mini_token=" + MiniToken);
            req.Append("&api_sig=" + signature);

            HttpWebRequest request = CreateRequest(restUrl + req.ToString());
            request.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            XmlDocument received = new XmlDocument();
            received.LoadXml(ReadResponseBody(response));
            /**
             <?xml version="1.0" encoding="utf-8"?>
             * <rsp stat="ok">
             * <auth>
             * <token>1005585-b77f58089b060afb</token>
             * <perms>write</perms>
             * <user nsid="69337239@N00" username="choomba" fullname="" />
             * </auth>
             * </rsp>
            /**/
            authToken = "fail";
            XmlNode xmltoken = received.SelectSingleNode("descendant::rsp");
            if (xmltoken.Attributes["stat"].Value == "ok")
            {
                XmlNode _authtoken = received.SelectSingleNode("descendant::auth/token");
                if (_authtoken != null)
                {
                    authToken = _authtoken.InnerText;

                }
                //XmlNode usernsid = received.SelectSingleNode("descendant::user");
                //_nsid = usernsid.Attributes["nsid"].Value;
                return authToken;
            }
            else
            {
                XmlNode err = received.SelectSingleNode("descendant::rsp/err");
                if (OnErrorResponse != null)
                    OnErrorResponse(this, new FlickrErrorArgs(err.Attributes["code"].Value,
                        err.Attributes["msg"].Value));
                return authToken;
            }

        }

        public void CheckToken()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrCheckToken), authToken);
        }

        public string UploadPhoto(string fullpath)
        {
            FileUpload(fullpath);
            return "null";
        }

        /*
         * based on flickr.net library
         */
        public string FileUpload(string fileName)
        {
            if (fileName != null && fileName != string.Empty)
            {
                string boundary = "TASKUBLOGI_MIME_" + DateTime.Now.ToString("yyyyMMddhhmmss");
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://www.flickr.com/services/upload/");
                req.Method = "POST";
                req.KeepAlive = false;
                req.Timeout = 100000;
                req.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
                StringBuilder sb = new StringBuilder();

                NameValueCollection parameters = new NameValueCollection();
                parameters.Add("api_key", apiKey);
                parameters.Add("auth_token", authToken);

                string[] keys = parameters.AllKeys;
                StringBuilder HashStringBuilder = new StringBuilder(sharedSecret, 2 * 1024);
                Array.Sort(keys);

                foreach (string key in keys)
                {
                    HashStringBuilder.Append(key);
                    HashStringBuilder.Append(parameters[key]);
                    sb.Append("--" + boundary + "\r\n");
                    sb.Append("Content-Disposition: form-data; name=\"" + key + "\"\r\n");
                    sb.Append("\r\n");
                    sb.Append(parameters[key] + "\r\n");
                }

                sb.Append("--" + boundary + "\r\n");
                sb.Append("Content-Disposition: form-data; name=\"api_sig\"\r\n");
                sb.Append("\r\n");
                sb.Append(SignRequest(HashStringBuilder.ToString()) + "\r\n");

                // Photo
                sb.Append("--" + boundary + "\r\n");
                sb.Append("Content-Disposition: form-data; name=\"photo\"; filename=\"" + fileName + "\"\r\n");
                sb.Append("Content-Type: image/jpeg\r\n");
                sb.Append("\r\n");

                UTF8Encoding encoding = new UTF8Encoding();

                byte[] postContents = encoding.GetBytes(sb.ToString());

                FileStream stream = new FileStream(fileName, FileMode.Open);
                photosize = stream.Length;
                byte[] photoContents = new byte[stream.Length];
                stream.Read(photoContents, 0, photoContents.Length);
                stream.Close();

                byte[] postFooter = encoding.GetBytes("\r\n--" + boundary + "--\r\n");

                byte[] dataBuffer = new byte[postContents.Length + photoContents.Length + postFooter.Length];
                Buffer.BlockCopy(postContents, 0, dataBuffer, 0, postContents.Length);
                Buffer.BlockCopy(photoContents, 0, dataBuffer, postContents.Length, photoContents.Length);
                Buffer.BlockCopy(postFooter, 0, dataBuffer, postContents.Length + photoContents.Length, postFooter.Length);

                req.ContentLength = dataBuffer.Length;

                Stream resStream = req.GetRequestStream();

                int j = 1;
                int uploadBit = Math.Max(dataBuffer.Length / 100, 50 * 1024);
                int uploadSoFar = 0;

                for (int i = 0; i < dataBuffer.Length; i = i + uploadBit)
                {
                    int toUpload = Math.Min(uploadBit, dataBuffer.Length - i);
                    uploadSoFar += toUpload;

                    resStream.Write(dataBuffer, i, toUpload);

                    if ((OnUploadProgress != null) && ((j++) % 5 == 0 || uploadSoFar == dataBuffer.Length))
                    {
                        OnUploadProgress(this, new UploadProgressEventArgs(i + toUpload, uploadSoFar == dataBuffer.Length));
                    }
                }
                resStream.Close();

                HttpWebResponse res = (HttpWebResponse)req.GetResponse();

                return ReadResponseBody(res);
            }
            else
                return "no files";
        }

        public string FileUpload(string fileName, NameValueCollection parameters)
        {
            string boundary = "TASKUBLOGI_MIME_" + DateTime.Now.ToString("yyyyMMddhhmmss");
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://www.flickr.com/services/upload/");
            req.Method = "POST";
            req.KeepAlive = false;
            req.Timeout = 100000;
            req.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
            StringBuilder sb = new StringBuilder();

            //NameValueCollection parameters = new NameValueCollection();
            parameters.Add("api_key", apiKey);
            parameters.Add("auth_token", authToken);

            string[] keys = parameters.AllKeys;
            StringBuilder HashStringBuilder = new StringBuilder(sharedSecret, 2 * 1024);
            Array.Sort(keys);

            foreach (string key in keys)
            {
                HashStringBuilder.Append(key);
                HashStringBuilder.Append(parameters[key]);
                sb.Append("--" + boundary + "\r\n");
                sb.Append("Content-Disposition: form-data; name=\"" + key + "\"\r\n");
                sb.Append("\r\n");
                sb.Append(parameters[key] + "\r\n");
            }

            sb.Append("--" + boundary + "\r\n");
            sb.Append("Content-Disposition: form-data; name=\"api_sig\"\r\n");
            sb.Append("\r\n");
            sb.Append(SignRequest(HashStringBuilder.ToString()) + "\r\n");

            // Photo
            sb.Append("--" + boundary + "\r\n");
            sb.Append("Content-Disposition: form-data; name=\"photo\"; filename=\"" + fileName + "\"\r\n");
            sb.Append("Content-Type: image/jpeg\r\n");
            sb.Append("\r\n");

            UTF8Encoding encoding = new UTF8Encoding();

            byte[] postContents = encoding.GetBytes(sb.ToString());

            FileStream stream = new FileStream(fileName, FileMode.Open);
            photosize = stream.Length;
            byte[] photoContents = new byte[stream.Length];
            stream.Read(photoContents, 0, photoContents.Length);
            stream.Close();

            byte[] postFooter = encoding.GetBytes("\r\n--" + boundary + "--\r\n");

            byte[] dataBuffer = new byte[postContents.Length + photoContents.Length + postFooter.Length];
            Buffer.BlockCopy(postContents, 0, dataBuffer, 0, postContents.Length);
            Buffer.BlockCopy(photoContents, 0, dataBuffer, postContents.Length, photoContents.Length);
            Buffer.BlockCopy(postFooter, 0, dataBuffer, postContents.Length + photoContents.Length, postFooter.Length);

            req.ContentLength = dataBuffer.Length;

            Stream resStream = req.GetRequestStream();

            int j = 1;
            int uploadBit = Math.Max(dataBuffer.Length / 100, 50 * 1024);
            int uploadSoFar = 0;

            for (int i = 0; i < dataBuffer.Length; i = i + uploadBit)
            {
                int toUpload = Math.Min(uploadBit, dataBuffer.Length - i);
                uploadSoFar += toUpload;

                resStream.Write(dataBuffer, i, toUpload);

                if ((OnUploadProgress != null) && ((j++) % 5 == 0 || uploadSoFar == dataBuffer.Length))
                {
                    OnUploadProgress(this, new UploadProgressEventArgs(i + toUpload, uploadSoFar == dataBuffer.Length));
                }
            }
            resStream.Close();

            HttpWebResponse res = (HttpWebResponse)req.GetResponse();

            return ReadResponseBody(res);
        }

        public void GetPhotoSets(string userid)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrPhotosetsGetList), userid);
        }

        public void GetSetPhotos(string photosetid)
        {
            //return this.flickrGetPhotos(photosetid);
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrPhotosetsGetPhotos), photosetid);
        }

        public void GetPhotosNotInSet()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrPhotosGetNotInSet));
        }

        public void GetContacts()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrContactsGetList));
        }

        public Bitmap GetContactBuddyIcons(flickrContact contact)
        {
            Bitmap contactBm = new System.Drawing.Bitmap(48, 48);
            if (File.Exists(HelperUtils.ApplicationPath + "\\cache\\flickr\\" + contact.Id + ".jpg"))
            {
                contactBm = new Bitmap(
                    HelperUtils.ApplicationPath + "\\cache\\flickr\\" + contact.Id + ".jpg");
            }
            else
            {
                XMLRPCCommunication downloader = new XMLRPCCommunication(contact.BuddyIconUrl);


                contactBm = new System.Drawing.Bitmap(
                    downloader.DownloadBitmap()
                    );

                contactBm.Save(
                    HelperUtils.ApplicationPath + "\\cache\\flickr\\" + contact.Id + ".jpg",
                    System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            return contactBm;
        }

        private void addContactBuddyIconToImageList(string p)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public flickrPhoto[] GetPhotosContext(string photoid)
        {
            return this.flickPhotosGetContext(photoid);
        }

        public flickrPhoto[] GetPhotosetContext(string photosetid, string photoid)
        {
            return this.flickPhotosetsGetContext(photosetid, photoid);
        }

        public void GetPeoplePublicPhotos(string nsid)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrPeopleGetPublicPhotos), nsid);
        }

        public void CreatePhotoSet(flickrPhotoset set)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrPhotoSetsCreate), set);
        }


        public void DeletePhotoSet(string id)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(flickrPhotoSetsDelete), id);
        }



        #endregion

        #region [ Upload Event and Delegate ]

        /// <summary>
        /// UploadProgressHandler is fired during a synchronous upload process to signify that 
        /// a segment of uploading has been completed. This is approximately 50 bytes. The total
        /// uploaded is recorded in the <see cref="UploadProgressEventArgs"/> class.
        /// </summary>
        public event UploadProgressHandler OnUploadProgress;

        #endregion




    }

    public delegate void flickrResponseHandler(object sender, EventArgs args);

    public delegate void flickrErrorHandler(object sender, FlickrErrorArgs args);

    public class FlickrMsgArgs : EventArgs
    {

    }

    public class FlickrErrorArgs : EventArgs
    {
        private string _errno;
        public string ErrorNumber
        {
            get { return _errno; }
        }

        private string _msg;
        public string ErrorMessage
        {
            get { return _msg; }
        }

        public FlickrErrorArgs(string error, string message)
        {
            _errno = error;
            _msg = message;
        }

    }
}
